#pragma once
#include <iostream>
#include <algorithm>
#include <queue>
#include <jsoncpp/json/json.h>
#include <mutex>
#include "../comm/httplib.h"
#include "oj_model.hpp"
#include "oj_view.hpp"

namespace ns_control
{
    using namespace httplib;
    using namespace ns_model;
    using namespace ns_view;

    const std::string conpile_host_conf_path = "./conf/CompileHost.conf";

    class CompileHost
    {
    private:
        size_t load = 0;
        std::mutex _mtx;

    public:
        std::string id;
        std::string ip;
        size_t port;
        void AddLoad()
        {
            _mtx.lock();
            ++load;
            _mtx.unlock();
        }
        void SubLoad()
        {
            _mtx.lock();
            --load;
            _mtx.unlock();
        }
        void CleanLoad()
        {
            _mtx.lock();
            load = 0;
            _mtx.unlock();
        }
        // 非线程安全，可能读到旧的数据
        size_t GetLoad() const
        {
            return load;
        }
    };
    class ManageHsot
    {
    private:
        std::mutex _mtx;
        std::vector<CompileHost *> ComHosts;
        std::unordered_map<std::string, int> id_host;
        std::vector<int> OnComHost;
        // std::vector<int> OffComHost;

    public:
        ManageHsot()
        {
            std::vector<std::string> v;
            FileUtil::ReadLineFile(v, conpile_host_conf_path);
            // 将配置文件主机加载到内存
            for (int i = 0; i < v.size(); i++)
            {
                // LOG(DEBUG) << i << "\n";
                std::vector<std::string> id_ip_port;
                StrUtil::SplitString(id_ip_port, v[i], " ");
                CompileHost *cph = new CompileHost();
                cph->id = id_ip_port[0];
                cph->ip = id_ip_port[1];
                cph->port = atoi(id_ip_port[2].c_str());
                // LOG(DEBUG) << "id:" << cph->id << " ip:" << cph->ip << " port:" << cph->port << "\n";
                ComHosts.push_back(cph);
                // 根据id找下标
                id_host.insert({cph->id, i});
                // 默认全主机上线
                OnComHost.push_back(i);
            }
            // for(auto i : OnComHost)
            // {
            //     std::cout << i << ":" << ComHosts[i]->id << ":" << ComHosts[i]->port << std::endl;
            // }
        }
        ~ManageHsot()
        {
            for (auto i : ComHosts)
            {
                delete i;
            }
        }

        bool OnHost(const std::string &id)
        {
            _mtx.lock();
            auto it = id_host.find(id);
            if (it == id_host.end())
            {
                _mtx.unlock();
                LOG(WARNING) << "未配置主机尝试上线" << "\n";
                return false;
            }
            OnComHost.push_back(it->second);
            _mtx.unlock();
            return true;
        }
        bool OffHost(const std::string &id)
        {
            _mtx.lock();
            auto del = id_host.find(id);
            if (del == id_host.end())
            {
                _mtx.unlock();
                LOG(WARNING) << "未配置主机尝试上线" << "\n";
                return false;
            }
            for (auto it = OnComHost.begin(); it != OnComHost.end(); ++it)
            {
                if (del->second == *it)
                {
                    // 主机下线，负载清除
                    ComHosts[OnComHost[*it]]->CleanLoad();
                    it = OnComHost.erase(it); // 注意删除迭代器失效，但是这里删完就完了，可以不处理。
                    break;
                }
            }
            _mtx.unlock();
            return true;
        }
        void TryAllComeOnline()
        {
            int n = ComHosts.size();
            for (int i = 0; i < n; ++i)
            {
                OnHost(ComHosts[i]->id);
            }
        }

        // 输出型参数，返回选择的主机
        bool SmartChoice(CompileHost **host)
        {
            _mtx.lock();
            int n = OnComHost.size();
            if (n == 0)
            {
                _mtx.unlock();
                LOG(ERROR) << "全部编译主机下线，请尽快维修" << "\n";
                TryAllComeOnline();
                return false;
            }
            int min_load = ComHosts[OnComHost[0]]->GetLoad();
            (*host) = ComHosts[OnComHost[0]];
            for (int i = 1; i < OnComHost.size(); i++)
            {
                int now_load = ComHosts[OnComHost[i]]->GetLoad();
                if (now_load < min_load)
                {
                    min_load = now_load;
                    // LOG(DEBUG) << "交换前选择的主机id:" << (*host)->id << " ip:" << (*host)->ip << " port:" << (*host)->port << " load:" << (*host)->GetLoad() << "\n";
                    (*host) = ComHosts[OnComHost[i]];
                    // LOG(DEBUG) << "交换后选择的主机id:" << (*host)->id << " ip:" << (*host)->ip << " port:" << (*host)->port << " load:" << (*host)->GetLoad() << "\n";
                }
            }
            // 加负载
            (*host)->AddLoad();
            // LOG(DEBUG) << "最后选择的主机id:" << (*host)->id << " ip:" << (*host)->ip << " port:" << (*host)->port << " load:" << (*host)->GetLoad() << "\n";

            _mtx.unlock();
            return true;
        }
        // bool recycling()
        // {
        //     return true;
        // }
    };

    class Control
    {
    private:
        Model _model;
        View _view;
        ManageHsot _mght;

    private:
        // 判断是否登录
        bool IsLogin(const std::string &sessionId)
        {
            std::string session;
            if (_model._mgusr.SessionIdIsValib(sessionId))
                return true;
            else
                return false;
        }

    public:
        // html:输出型参数,返回需要的htnl页面字符串
        // sessionId: 确认客户端的用户标识
        bool AllQuestions(std::string *html, const std::string &sessionId)
        {
            // 通过model获取所有题目
            std::vector<Question> v;
            if (!_model.GetQuestions(v))
            {
                return false;
            }
            // 判断是否登录
            std::string session;
            if (IsLogin(sessionId))
            {
                User usr;
                _model._mgusr.SeidGetUsr(sessionId, usr);
                session = usr.idname;
            }
            else
                session = "登录";

            // 通过view将获取的题目渲染进html
            _view.QuestionList(v, html, session);
            return true;
        }

        /*************************************************
         * 1.html:输出型参数,返回需要的htnl页面字符串
         * 2.number: 题号
         * 3.sessionId: 确认客户端的用户标识
         *************************************************/
        bool OneQuestion(std::string *html, const std::string &number, const std::string &sessionId)
        {
            // 通过model获取题目
            Question q;
            _model.GetOneQuestion(q, number);

            // 通过view将获取的题目渲染进html
            if (!_model.GetOneQuestion(q, number))
            {
                return false;
            }
            // 判断是否登录
            // 判断是否登录
            std::string session;
            if (IsLogin(sessionId))
            {
                User usr;
                _model._mgusr.SeidGetUsr(sessionId, usr);
                session = usr.idname;
            }
            else
                session = "登录";

            _view.OneQuestion(q, html, session);
            // LOG(DEBUG) << *html << "\n";

            return true;
        }

        /*************************************************
         * 1.number:题号。
         * 2.in_json结构:code(必填):为用户填写的代码。
         * 3.out_json: 输出型参数，返回判题返回的内容
         * 4.sessionId: 确认客户端的用户标识
         * 返回值：
         * 0:正常返回（编译服务器正常）
         * -1:编译主机全部下线
         * -2:客户端未登录或登录已过期
         * -3:未知错误（可扩展）
         *************************************************/
        int ParseQuestion(const std::string &number, const std::string &in_json, std::string *out_json, const std::string &sessionId)
        {
            // 正常来说前端已经防止了未登录判题，但是前端可能被恶意篡改，要处理一下
            if (!IsLogin(sessionId))
                return -2;

            Question q;
            _model.GetOneQuestion(q, number);
            // 解析前端json串
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            std::string headers = in_value["code"].asString();
            // std::string user_in = in_value["user_in"].asString();//用户输入
            // 拼接用户代码与测试代码
            q.code = headers + q.tail;
            // LOG(DEBUG) << q.code << "\n";
            // 构造给编译接口的json串
            Json::Value compile_value;
            compile_value["code"] = q.code;
            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;
            Json::StyledWriter writer;
            std::string compile_json = writer.write(compile_value);

            // 开始编译，选择主机
            CompileHost *host;
            while (true)
            {
                if (!_mght.SmartChoice(&host))
                {
                    return -1;
                }
                LOG(INFO) << "选择的编译主机id:" << host->id << " ip:" << host->ip << " port:" << host->port << " load:" << host->GetLoad() << "\n";
                Client cli(host->ip, host->port);
                cli.set_connection_timeout(3);
                auto res = cli.Post("/compile_and_run", compile_json, "application/json;charset=utf-8");
                if (res != nullptr)
                {
                    // LOG(DEBUG) << "编译返回josn串:" << ret->body << "\n";
                    // 返回处理完成的json串
                    if (res->status == 200)
                    {
                        // 编译完后负载减轻
                        host->SubLoad();
                        *out_json = res->body;
                        LOG(INFO) << "请求编译和运行服务成功..." << "\n";
                        //
                        return 0;
                    }
                    LOG(WARNING) << "完成调用编译程序,但返回码错误,返回码:" << res->status << "\n";
                }
                else
                {
                    // 主机可能下线了,下线主机
                    LOG(WARNING) << "一台编译主机已下线,主机id:" << host->id << " ip:" << host->ip << " port:" << host->port << " load:" << host->GetLoad() << "\n";
                    _mght.OffHost(host->id);
                }
            }
            LOG(ERROR) << "未知错误" << "\n";
            return -3;
        }

        bool UsrLogin(const std::string &in_json, std::string *out_json)
        {
            LOG(DEBUG) << "in_json:" << in_json << "\n";
            Json::Value inValue;
            Json::Reader reader;
            reader.parse(in_json, inValue);
            std::string idname = inValue["username"].asString();
            std::string password = inValue["password"].asString();
            std::string sessionId;
            Json::Value outValue;
            Json::StyledWriter write;
            if (_model._mgusr.LoginUsr(idname, password, sessionId))
            {
                outValue["IsLogin"] = true;
                outValue["sessionId"] = sessionId;
            }
            else
            {
                outValue["IsLogin"] = false;
            }
            *out_json = write.write(outValue);
            LOG(DEBUG) << "out_json:" << *out_json << "\n";
            return true;
        }
        bool UsrRegister(const std::string &in_json, std::string *out_json)
        {
            LOG(DEBUG) << "in_json:" << in_json << "\n";
            Json::Value inValue;
            Json::Reader reader;
            reader.parse(in_json, inValue);
            std::string idname = inValue["username"].asString();
            std::string password = inValue["password"].asString();
            std::string email = inValue["email"].asString();
            Json::Value outValue;
            Json::StyledWriter write;
            if (_model._mgusr.RegistUsr(idname, password, email))
            {
                outValue["IsRegist"] = true;
            }
            else
            {
                outValue["IsRegist"] = false;
            }
            *out_json = write.write(outValue);
            LOG(DEBUG) << "out_json:" << *out_json << "\n";
            return true;
        }

        bool ChatPage(std::string *html, const std::string &sessionId)
        {
            // 判断是否登录
            std::string session;
            if (IsLogin(sessionId))
            {
                User usr;
                _model._mgusr.SeidGetUsr(sessionId, usr);
                session = usr.idname;
            }
            else
                session = "登录";

            // 通过view将获取的题目渲染进html
            _view.ChatPage(html, session);
            return true;
        }
        bool ChatSend(const std::string in_json, std::string *out_json)
        {
            LOG(DEBUG) << "in_json:" << in_json << "\n";
            Json::Value inValue;
            Json::Reader reader;
            reader.parse(in_json, inValue);
            std::string text = inValue["text"].asString();
            std::string sessionId = inValue["sessionId"].asString();
            Json::Value outValue;
            Json::StyledWriter write;
            // 正常来说前端已经防止了未登录判题，但是前端可能被恶意篡改，要处理一下
            if (!IsLogin(sessionId))
            {
                outValue["success"] = false;
                *out_json = write.write(outValue);
                return false;
            }
            std::string idname;
            time_t nowtime;
            std::string chatText;
            User usr;
            _model._mgusr.SeidGetUsr(sessionId, usr);
            idname = usr.idname;
            nowtime = time(nullptr);
            chatText = text;
            Message m(idname, chatText, nowtime);
            //对消息进行管理
            _model._mgusr.HaveNewMessage(idname,m);

            outValue["success"] = true;
            outValue["idname"] = idname;
            outValue["chatText"] = chatText;
            outValue["nowtim"] = nowtime;
            *out_json = write.write(outValue);
            LOG(DEBUG) << "out_json:" << *out_json << "\n";

            return true;
        }
        bool ChatRecv(const std::string in_json, std::string *out_json)
        {
            Json::Value inValue;
            Json::Reader reader;
            reader.parse(in_json, inValue);
            std::string sessionId = inValue["sessionId"].asString();
            Json::Value outValue;
            Json::StyledWriter write;
            // 正常来说前端已经防止了未登录判题，但是前端可能被恶意篡改，要处理一下
            if (!IsLogin(sessionId))
            {
                outValue["success"] = false;
                *out_json = write.write(outValue);
                return false;
            }
            std::string idname;
            User usr;
            _model._mgusr.SeidGetUsr(sessionId, usr);
            idname = usr.idname;
            Message message;
            //如果没有消息直接返回
            if(!_model._mgusr.UserGetOneMessage(idname,message))
            {
                outValue["success"] = false;
                *out_json = write.write(outValue);
                return false;
            }
            outValue["success"] = true;
            outValue["idname"] = message.name;
            outValue["chatText"] = message.text;
            outValue["nowtim"] = message.sendtime;
            *out_json = write.write(outValue);
            return true;
        }
    };
}
